package com.gitee.xmhzzz.component.tcp.server.service;


import cn.hutool.core.util.StrUtil;
import com.gitee.xmhzzz.component.model.message.BaseMsg;
import com.gitee.xmhzzz.component.model.message.CommonMsg;
import com.gitee.xmhzzz.component.model.req.CommonReq;
import com.gitee.xmhzzz.component.tcp.server.config.NettyServerProperties;
import com.gitee.xmhzzz.component.tcp.server.context.ChannelContext;
import com.gitee.xmhzzz.component.tcp.server.dto.ChannelAttr;
import com.gitee.xmhzzz.component.tcp.server.util.AlgorithmUtil;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j;

import java.security.GeneralSecurityException;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Sharable
public class TcpServiceHandler extends ChannelInboundHandlerAdapter {

    /**
     * 与服务端建立连接的客户端信息，后续与服务端有数据通信就会移除，如果超过2分钟都没有通信，将会被关闭连接。
     */
    public static Map<String, ChannelContext.ChannelInfo> establishedMap = new ConcurrentHashMap<>();

    /**
     * 发送了请求随机字符的客户端信息，后续发送登录请求就会移除，如果超过2分钟都没有登录请求，将会被关闭连接。
     */
    public static Map<String, ChannelContext.ChannelInfo> randomRequestMap = new ConcurrentHashMap<>();

    private NettyServerProperties properties;




    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
        String clientId = ctx.channel().id().toString();
        ChannelContext.ChannelInfo clientInfo = new ChannelContext.ChannelInfo();
        clientInfo.setChannel(ctx);
        clientInfo.setLastUpdated(LocalDateTime.now());
        establishedMap.put(clientId, clientInfo);
        log.info("通道连接设备上线TcpServiceHandler.channelActive channelId[{}]", clientId);
    }


    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        super.channelInactive(ctx);
        ctx.channel().close();
        ctx.close();
        ChannelContext.removeClient(ctx.channel().id().toString());
        log.info("通道连接关闭TcpServiceHandler.channelInactive channelId[{}]", ctx.channel().id().toString());
    }


    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        ctx.channel().close();
        ctx.close();
        ChannelContext.removeClient(ctx.channel().id().toString());
        log.error("通道异常关闭TcpServiceHandler.exceptionCaught channelId[{}] exception[{}]", ctx.channel().id().toString(), cause.getMessage(), cause);
    }


    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        super.handlerRemoved(ctx);
        ctx.channel().close();
        ctx.close();
        ChannelContext.removeClient(ctx.channel().id().toString());
        log.info("通道移除关闭TcpServiceHandler.HandlerRemoved channelId[{}]", ctx.channel().id().toString());
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        if (!channel.isWritable()) {
            log.info("出站流量大于高水位线，关闭自动读写. channelId [{}]", channel.id());
            channel.config().setAutoRead(false);
        }
    }

    @Override
    public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        if (channel.isWritable()) {
            log.info("出站流量小于低水位线，重新开启自动读写. channelId [{}]", channel.id());
            channel.config().setAutoRead(true);
        }
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        String clientId = ctx.channel().id().toString();
        try {
            if (msg == null) {
                log.info("通道接收数据异常，数据为Null. class[TcpServiceHandler.channelRead]");
                return;
            }
            String receiveMsg = msg.toString();
            log.info("通道接收数据TcpServiceHandler.channelRead  channelId[{}] data[{}]", clientId, receiveMsg);
            if (StrUtil.isEmpty(receiveMsg)) {
                return;
            }
            BaseMsg baseMsg = new Gson().fromJson(receiveMsg, BaseMsg.class);
            establishedMap.remove(clientId);
            ChannelContext.ChannelInfo client = ChannelContext.getClient(clientId);
            String appSecret = null;
            String appId = null;
            if (client == null) {
                appId = baseMsg.getAppId();
                if (StrUtil.isNotEmpty(baseMsg.getAppId())) {
                    log.warn("通道关闭TcpServiceHandler.channelRead  未登录时app参数不能为空 channelId[{}] data[{}]", clientId, receiveMsg);
                    ctx.channel().close();
                    ctx.close();
                    return;
                }

                appSecret = properties.getAppSecret(baseMsg.getAppId());
                if (StrUtil.isNotEmpty(appSecret)) {
                    // log.warn("通道关闭TcpServiceHandler.channelRead  App不存在 channelId[{}] data[{}] app[{}]", clientId, receiveMsg, ar.getApp());
                    ctx.channel().close();
                    ctx.close();
                    return;
                }
            } else {
                ChannelAttr.setDeviceId(ctx.channel(),client.getDeviceCode());
                appSecret = client.getAppSecret();
                appId = client.getAppId();
            }
            CommonMsg commonMsg = null;
            String msgStr = null;

            try {
                msgStr = AlgorithmUtil.aesDecrypt(baseMsg.getMsg(), appSecret);
            } catch (GeneralSecurityException e) {
                log.warn("通道异常,解密失败GeneralSecurityException channelId[{}] data[{}] appId[{}] exception[{}]", clientId, receiveMsg, appId, e.getMessage(), e);
                return;
            }
            log.info("通道数据解密 channelId[{}]  data[{}] appId[{}] decryptData[{}] ", clientId, receiveMsg, appId, msgStr);
            commonMsg = new Gson().fromJson(msgStr, CommonMsg.class);
            if (commonMsg.getTag() == null) {
                // log.warn("通道异常关闭,action参数不能为空 channelId[{}]   data[{}] appId[{}] decryptData[{}] ", clientId, receiveMsg, redisApp.getAppId(), data);
                ctx.channel().close();
                ctx.close();
                return;
             } //else if (ad.getA().equals(ActionConstants.RANDOM)) {
            //     ClientChannelInfo clientInfo = new ClientChannelInfo();
            //     clientInfo.setChannel(ctx);
            //     clientInfo.setLastUpdated(LocalDateTime.now());
            //     randomRequestMap.put(clientId, clientInfo);
            // } else if (ad.getA().equals(ActionConstants.LOGIN)) {
            //     randomRequestMap.remove(clientId);
            // } else {
            //     if (!ChannelContext.addConnect(ctx)) {
            //         log.debug("连接通道不一致. addConnect[{}] channelId[{}] actionData[{}]", true, clientId, msgStr);
            //         return;
            //     }
            // }
            //
            //
            // ActionFactory actionFactory = ApplicationContextUtil.getBean(ActionFactory.class);
            // ActionData actionData = new Gson().fromJson(data, ActionData.class);
            // IActionService actionService = actionFactory.getActionService(ad.getA());
            // if (actionService != null) {
            //     actionService.handle(ctx, redisApp, actionData);
            // } else {
            //     log.info("找不到对应的业务处理器 channelId[{}]  actionData[{}]", clientId, data);
            // }
        } catch (UnsupportedOperationException uoe) {
            log.warn("通道异常,UnsupportedOperationException channelId[{}] uniqueCode[{}] exception[{}]", clientId, uoe.getMessage(), uoe);
        } catch (NumberFormatException e) {
            log.warn("数据转换异常 channelId[{}]  exception[{}]", clientId, e.getMessage(), e);
        } catch (JsonSyntaxException e) {
            log.warn("JSON解析异常 channelId[{}] exception[{}]", clientId, e.getMessage(), e);
        } catch (Exception e) {
            log.error("通道异常 channelId[{}]  exception[{}]", clientId, e.getMessage(), e);
        } finally {
            ReferenceCountUtil.release(msg);
        }
    }

}
